위상 정렬
위상 정렬이란?
위상 정렬(topological sorting)은 유향 그래프의 꼭짓점들을 변의 방향을 거스르지 않도록 나열하는 것을 의미한다. 가장 쉬운 예로 대학교 선수과목 구조를 예로 들 수 있다. 만약 특정 수강 과목에 선수과목이 있다면 그 선수 과목부터 수강해야 하므로, 특정 과목들을 수강해야할 때 위상 정렬을 통해 올바른 수강 순서를 찾아낼 수 있다.
이처럼 선후 관계가 정의된 그래프 구조 상에서 선후 관계에 따라 정렬하기 위해서 위상정렬을 사용할 수 있다.
위상정렬 여러개의 답이 존재할 수 있고, 사이클이 없는 방향그래프(DAG)
에만 적용이 가능하다는 특징이 있다.
위상 정렬 수행과정
위상정렬 알고리즘은 두가지 해결책을 낸다는 특징이 있다.
- 현재 그래프는 위상 정렬이 가능한지 판단한다.
- 위상 정렬이 가능하다면 그 결과는 무엇인지 도출한다.
또한, 위상정렬을 수행하는 알고리즘은 크게 스택과 큐방식이 존재한다. 여기서는 큐방법을 이용해 위상정렬을 구현해보도록 하겠다.
위상정렬의 수행과정은 다음과 같다.
- 진입차수가 0인 정점을 큐에 삽입한다.
- 큐에서 원소를 꺼내 연결된 모든 간선을 제거한다.
- 간선 제거 이후에 진입차수가 0이 된 정점을 큐에 삽입한다.
- 큐가 빌 때까지 2번 ~ 3번 과정을 반복한다. 모든 원소를 방문하기 전에 큐가 빈다면 사이클이 존재하는 것이고, 모든 원소를 방문했다면 큐에서 꺼낸 순서가 위상정렬의 결과이다.
위상정렬 시뮬레이션
예제로 풀어보는 위상정렬
이번에 풀어볼 문제는 백준의 줄 세우기 문제이다. 이 문제를 통해 위상정렬을 익혀보도록 하자.
문제
N명의 학생들을 키 순서대로 줄을 세우려고 한다. 각 학생의 키를 직접 재서 정렬하면 간단하겠지만, 마땅한 방법이 없어서 두 학생의 키를 비교하는 방법을 사용하기로 하였다. 그나마도 모든 학생들을 다 비교해 본 것이 아니고, 일부 학생들의 키만을 비교해 보았다.
일부 학생들의 키를 비교한 결과가 주어졌을 때, 줄을 세우는 프로그램을 작성하시오.
입력
첫째 줄에 N(1 ≤ N ≤ 32,000), M(1 ≤ M ≤ 100,000)이 주어진다. M은 키를 비교한 회수이다. 다음 M개의 줄에는 키를 비교한 두 학생의 번호 A, B가 주어진다. 이는 학생 A가 학생 B의 앞에 서야 한다는 의미이다.
학생들의 번호는 1번부터 N번이다.
예제 입력
3 2
1 3
2 3
풀이
from collections import deque
n, m = map(int, input().split())
graph = [[] for _ in range(n + 1)]
inDeg = [0] * (n + 1)
q = deque()
result = []
for _ in range(m):
x, y = map(int, input().split())
graph[x].append(y) # 그래프를 만든다.
inDeg[y] += 1 # 진입차수 세팅
for i in range(1, n + 1): # 진입차수가 0인 것 부터 큐에 넣는다.
if inDeg[i] == 0:
q.append(i)
while q: # 큐를 돌면서 위상정렬 수행
a = q.popleft()
result.append(a)
for x in graph[a]:
inDeg[x] -= 1
if inDeg[x] == 0:
q.append(x)
print(*result)
위상정렬 시간복잡도
위상정렬의 시간 복잡도는 O(V+E)
이다. 정점의 갯수 + 간선의 갯수만큼 소요되기 때문에 매우 빠른 알고리즘 중 하나이다.
위상정렬을 더 풀고 싶다면?
나올 수 있는 면접 질문
- 위상정렬이란 무엇인가요?
- 위상정렬 알고리즘을 설명해보세요.
- 위상정렬의 시간복잡도는 어떻게 되나요?
참고
기여자
Kyun Heo
📦